package org.simplesocket.simplenettymtbatis.inject;

import org.apache.ibatis.session.SqlSession;
import org.simplesocket.simplenettymtbatis.SimpleNettyMybatisContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MapperProxy implements InvocationHandler {
    
    /**
     * JDK动态代理，代理了每个 @Router 类中被 @InjectMapper 注解
     * 的成员变量，初始化它们的值为 MapperProxy，并且在执行 Mybatis 的方法的时候，
     * 我们动态的从 sqlSession 中获取mapper来实际执行，原理是：
     * <p>
     * private UserMapper mapper -> mapper.getUsers() -> Proxy{获取mapper的实例，也就是从数据库连接池中获取} -->
     * <p>
     * --> 连接池中的Mapper实例(sqlSession.getMapper(Mapper))，proxy再去invoke(mapper实例，参数)得到结果 -->
     * <p>
     * --> 返回结果
     * <p>
     * 实际请求走向：
     *      业务逻辑 -> mapper.getData() -> Proxy代理 -> 真正的mapper.getData()
     * <p>
     *      在Proxy代理中我们就可以做很多操作了，这里的原理其实就是拦截对 mapper.getData() 的调用，在这个调用前后做一些操作，
     *      在本项目中，我们做的操作就是从数据库连接池中获取真正的 mapper 对象，在执行完sql后释放 sqlSession。
     * <p>
     * 为什么初始化数据库成员变量的值为 MapperProxy？
     *      因为代理对象其实可以看作被代理对象的子类（实现类），有点类似于 Animal dog = new Dog();
     *      Proxy相当于实现了 Mybatis 的 Mapper，只是我们在Proxy中并未真正实现 Mybatis 的 Mapper，
     *      在做完我们的操作后，我们又再次调用了 Mybatis 的原生接口Mapper，让Mybatis自己去找实现
     */
    
    private final Class<?> clazz;
    
    private final SimpleNettyMybatisContext mybatisContext;
    
    public MapperProxy(Class<?> clazz, SimpleNettyMybatisContext mybatisContext) {
        this.clazz = clazz;
        this.mybatisContext = mybatisContext;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession sqlSession = mybatisContext.getDatabase().sqlSession();
        Object invokeResult = method.invoke(sqlSession.getMapper(clazz), args);
        sqlSession.close();
        return invokeResult;
    }
}
